﻿using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using MahApps.Metro.Controls;
using Red.Controls.Extensions;
using Red.Controls.ViewModels;

namespace Red.Controls
{
    /// <summary>
    /// Interaction logic for OverlayControl.xaml
    /// </summary>

    public partial class OverlayControl : MetroWindow
    {
        #region Data

        private static Brush _mouseTransparentBrush = new SolidColorBrush( new Color { R = 0, G = 0, B = 0, A = 0 } );
        private static Brush _mouseOpaqueBrush = new SolidColorBrush( new Color { R = 127, G = 127, B = 127, A = 1 } );

        private FrameworkElement _placementTarget;
        private BackgroundMode _backgroundMode = BackgroundMode.Unknown;
        private Brush _defaultBackgroud;

        private bool _error = false;
        private BackgroundMode _pendingBackgroundMode = BackgroundMode.Unknown;

        #endregion Data

        #region Properties
        public UIElement OverlayContent
        {
            get { return (UIElement)GetValue( OverlayContentProperty ); }
            set { SetValue( OverlayContentProperty, value ); }
        }

        public static readonly DependencyProperty OverlayContentProperty =
        DependencyProperty.Register
        (
            "OverlayContent",
            typeof( object ),
            typeof( OverlayControl )
        );
        #endregion

        enum BackgroundMode
        {
            Unknown,
            MouseTransparent,
            MouseOpaque,
            AcceptingDrop
        }

        #region C-tor

        public OverlayControl( FrameworkElement placementTarget )
        {
            InitializeComponent();

            _placementTarget = placementTarget;
            AllowsTransparency = true;

            SetBackgroundMode( BackgroundMode.MouseTransparent );
            DragBroadcaster.OnDragStarted += DragBroadcaster_OnDragStarted;
            DragBroadcaster.OnDragEnded += DragBroadcaster_OnDragEnded;

            _previewOptionsButton.Visibility = Visibility.Hidden;

            _defaultBackgroud = Background;
        }

        #endregion C-tor

        #region Public Methods

        public void ShowError( string errorTitle, string errorDescription, string details )
        {
            _errorControl.ErrorTitle = errorTitle;
            _errorControl.ErrorInfo = errorDescription;
            _errorControl.Visibility = Visibility.Visible;
            _errorControl.ErrorDetails = details;

           _error = true;
            SetBackgroundForError();
        }

        public void HideError()
        {
            _errorControl.Visibility = Visibility.Collapsed;

            if ( _error )
            {
                _error = false;
                SetBackgroundMode( _pendingBackgroundMode );
            }
        }

        #endregion

        #region Event Handlers

        private void DragBroadcaster_OnDragStarted( object sender, IDataObject draggedData )
        {
            SetBackgroundMode( BackgroundMode.MouseOpaque );
        }

        void DragBroadcaster_OnDragEnded( object sender, IDataObject draggedData )
        {
            SetBackgroundMode( BackgroundMode.MouseTransparent );
        }

        private async void Overlay_DragEnter( object sender, DragEventArgs e )
        {
            DataItem item = DataContext as DataItem;
            if ( item != null )
            {
                Point p = e.GetPosition( this );
                string formatsStr = ExtractDataAsStrings( e.Data );
                var accepted = await item.ExecuteAsync<bool>( "onDragEnter", formatsStr, p.X, p.Y );
                if ( accepted.Value )
                {
                    SetBackgroundMode( BackgroundMode.AcceptingDrop );
                }
            }
        }

        private async void Overlay_DragOver( object sender, DragEventArgs e )
        {
            DataItem item = DataContext as DataItem;
            if ( item != null )
            {
                Point p = e.GetPosition( this );
                string dataStr = ExtractDataAsStrings( e.Data );
                await item.ExecuteAsync( "onDragOver", dataStr, p.X, p.Y );
            }
        }

        private async void Overlay_DragLeave( object sender, DragEventArgs e )
        {
            DataItem item = DataContext as DataItem;
            if ( item != null )
            {
                SetBackgroundMode( BackgroundMode.MouseOpaque );
                string dataStr = ExtractDataAsStrings( e.Data );
                await item.ExecuteAsync( "onDragLeave", dataStr );
            }
        }

        private async void Overlay_Drop( object sender, DragEventArgs e )
        {
            DataItem item = DataContext as DataItem;
            e.Effects = DragDropEffects.None;
            if ( item != null )
            {
                Point p = e.GetPosition( this );
                string dataStr = ExtractDataAsStrings( e.Data );
                var result = await item.ExecuteAsync<bool>( "onDrop", dataStr, p.X, p.Y );
                if ( result.Value )
                    e.Effects = DragDropEffects.Copy;
            }
        }

        private void PreviewOptionsButton_Click( object sender, RoutedEventArgs e )
        {
            _optionsFlyout.IsOpen = true;
            _previewOptionsButton.Visibility = Visibility.Hidden;
        }

        private void OptionsFlyout_ClosingFinished( object sender, RoutedEventArgs e )
        {
            _previewOptionsButton.Visibility = Visibility.Visible;
        }

        private void Overlay_Loaded( object sender, RoutedEventArgs e )
        {
            _previewOptionsButton.Visibility = Visibility.Visible; 

            UpdateSize();
            UpdatePosition();

            Owner = GetWindow( _placementTarget );
            if ( Owner != null )
            {
                _placementTarget.SizeChanged += PlacementTarget_SizeChanged;
                Owner.LocationChanged += ParentWindow_PositionChanged;
                Owner.Closing += MainWindow_Closing;
            }
        }

        void PlacementTarget_SizeChanged( object sender, SizeChangedEventArgs e )
        {
            UpdateSize();
            UpdatePosition();
        }

        void ParentWindow_PositionChanged( object sender, EventArgs e )
        {
            UpdatePosition();
        }

        void MainWindow_Closing( object sender, System.ComponentModel.CancelEventArgs e )
        {
            CleanupParentHandlers();
            Close();
        }

        private void RestoreEngine_Executed( object sender, EventArgs e )
        {
            if ( Owner != null )
                UICommands.RestoreEngine.Execute( null, Owner );
        }

        private void Overlay_KeyDown( object sender, System.Windows.Input.KeyEventArgs e )
        {
            if ( e.Key == System.Windows.Input.Key.System && e.SystemKey == System.Windows.Input.Key.F4 )
            {
                e.Handled = true;
            }
        }

        #endregion Event handlers

        #region Private methods

        private string ExtractDataFormats( IDataObject obj )
        {
            return String.Join( ",", obj.GetFormats() );
        }

        private string ExtractDataAsStrings( IDataObject obj )
        {
            string[] dataStrings = obj.GetFormats().Select( format => obj.GetData( format ).ToString() ).ToArray();
            return String.Join( ",", dataStrings );
        }

        private void SetBackgroundForError()
        {
            Background = new SolidColorBrush( new Color() { A = 255 } );
            _marker.Visibility = Visibility.Hidden;
            _backgroundMode = BackgroundMode.Unknown;
        }

        private void SetBackgroundMode( BackgroundMode mode )
        {
            _pendingBackgroundMode = mode;
            if ( mode == _backgroundMode || _error )
            {
                return;
            }

            _backgroundMode = mode;

            switch ( mode )
            {
                case BackgroundMode.MouseTransparent:
                    Background = _mouseTransparentBrush;
                    _marker.Visibility = Visibility.Hidden;
                    break;
                case BackgroundMode.MouseOpaque:
                    Background = _mouseOpaqueBrush;
                    _marker.Opacity = 0.5;
                    _marker.Visibility = Visibility.Visible;
                    break;
                case BackgroundMode.AcceptingDrop:
                    Background = _mouseOpaqueBrush;
                    _marker.Opacity = 0.8;
                    _marker.Visibility = Visibility.Visible;
                    break;
            }
        }

        private void CleanupParentHandlers()
        {
            if ( Owner != null )
            {
                _placementTarget.SizeChanged -= PlacementTarget_SizeChanged;
                Owner.Closing -= MainWindow_Closing;
                Owner.LocationChanged -= ParentWindow_PositionChanged;
            }
        }

        private void UpdateSize()
        {
            if ( _placementTarget != null )
            {
                Width = _placementTarget.ActualWidth;
                Height = _placementTarget.ActualHeight;
            }
        }

        private void UpdatePosition()
        {
            if ( _placementTarget != null && PresentationSource.FromVisual( _placementTarget ) != null )
            {
                var location = _placementTarget.PointToScreen( new Point( 0, 0 ) );
                Left = location.X;
                Top = location.Y;
            }
        }
        #endregion Private methods
    }

}
